package main

import (
	"container/list"
	"errors"
	"fmt"
	"strings"
	"syscall"
	"time"
	"unsafe"
)

var (
	height = 30
	width  = 30

	origPos  COORD
	workArea SMALL_RECT
	food     SHORT
	empty    = make(map[SHORT]bool)
	snake    = list.New()
)

func main() {
	InitSyscall()
	StartAnimation()

	snakeHead := popCell()
	snake.PushFront(snakeHead)
	delete(empty, snakeHead)
	food = popCell()
	drawCell(snakeHead, "■")
	drawCell(food, "$")

	err := snakeRun()
	SetConsoleCursorPosition(COORD{origPos.X, origPos.Y + 2})
	if err != nil {
		fmt.Println(err)
	}
	fmt.Printf("得分：%v\n", (snake.Len()-1)*100)
}

func snakeRun() error {
	keypress := make(chan rune)
	go hanldeInput(keypress)

	direction := 's'
	for {
		select {
		case <-time.Tick(time.Millisecond * 200):
			break
		}
		newhead := snake.Front().Value.(SHORT)
		var kp rune
		select {
		case kp = <-keypress:
		default:
		}
		switch kp {
		case 'a':
			if direction != 'd' {
				direction = kp
			}
		case 'w':
			if direction != 's' {
				direction = kp
			}
		case 's':
			if direction != 'w' {
				direction = kp
			}
		case 'd':
			if direction != 'a' {
				direction = kp
			}
		case 'q':
			return nil
		}
		switch direction {
		case 'a':
			newhead = (newhead - (1 << 8))
		case 'w':
			newhead = newhead + 1
		case 's':
			newhead = newhead - 1
		case 'd':
			newhead = (newhead + (1 << 8))
		}

		_, exists := empty[newhead]
		if exists {
			snake.PushFront(newhead)
			delete(empty, newhead)
			drawCell(newhead, "■")

			if food != newhead {
				back := snake.Back()
				snake.Remove(back)
				tail := back.Value.(SHORT)
				empty[tail] = true
				drawCell(tail, " ")
			} else {
				food = popCell()
				if food == -1 {
					return errors.New("游戏结束")
				} else {
					drawCell(food, "$")
				}
			}

		} else {
			//游戏失败
			return errors.New("游戏失败")
		}
	}
	return nil
}

func StartAnimation() {
	for _, ch := range []string{"■", "  "} {
		for i := 0; i < height; i++ {
			for n := 0; n < width; n++ {
				if i == 0 {
					if n == 0 {
						fmt.Print("┏")
					} else if n == width-1 {
						fmt.Print("┓")
					} else {
						fmt.Print("━")
					}
				} else if i == height-1 {
					if n == 0 {
						fmt.Print("┗")
					} else if n == width-1 {
						fmt.Print("┛")
					} else {
						fmt.Print("━")
					}
				} else {
					if n == 0 || n == width-1 {
						fmt.Print("┃")
					} else {
						fmt.Print(ch)
					}
				}
			}
			fmt.Println("")
		}
		if ch != "  " {
			time.Sleep(time.Second * 1)

			origPos = GetConsoleCursorPosition()
			origPos.X += 2
			origPos.Y -= 2

			SetConsoleCursorPosition(COORD{0, origPos.Y + 2 - SHORT(height)})

			/* 	SetConsoleCursorPosition(COORD{origPos.X, origPos.Y + 1})
			fmt.Print("A")
			return */
		}
	}

	workArea.Left = 0
	workArea.Bottom = 0
	workArea.Right = SHORT((width - 3))
	workArea.Top = SHORT(height - 3)

	for i := workArea.Left; i <= workArea.Right; i++ {
		for n := workArea.Bottom; n <= workArea.Top; n++ {
			empty[((i << 8) | (n & 127))] = true
		}
	}
}

func popCell() SHORT {
	for k := range empty {
		return k
	}
	return -1
}

func drawCell(pos SHORT, ch string) {
	/* if err = checkArea(pos); err != nil {
		return err
	} */
	x := pos >> 8
	y := pos & 127

	err := SetConsoleCursorPosition(COORD{(x * 2) + origPos.X, origPos.Y - y})
	if err != nil {
		panic(err)
	}
	fmt.Print(ch)
}

func hanldeInput(keypress chan rune) {
	for {
		err := WaitForMultipleObjects([]syscall.Handle{Stdin})
		if err != nil {
			panic(err)
		}

		var r INPUT_RECORD
		ReadConsoleInput(&r)
		kr := (*KEY_EVENT_RECORD)(unsafe.Pointer(&r.Event))
		if kr.KeyDown == 0 {
			continue
		}

		switch kr.VirtualKeyCode {
		case VK_ARROW_UP:
			keypress <- 'w'
		case VK_ARROW_DOWN:
			keypress <- 's'
		case VK_ARROW_LEFT:
			keypress <- 'a'
		case VK_ARROW_RIGHT:
			keypress <- 'd'
		}

		if kr.UnicodeChar != 0 {
			keypress <- rune(strings.ToLower(string(kr.UnicodeChar))[0])
		}
	}
}
